package com.jacky.demo.widget.camera;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.SurfaceTexture;
import android.hardware.SensorManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.v4.view.MotionEventCompat;
import android.util.Log;
import android.util.Size;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Toast;

import com.jacky.demo.util.ToastUtil;
import com.jacky.log.Logger;

import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class Camera2Fragment extends Fragment {
    public static final String CAMERA_FRONT = "1";
    public static final String CAMERA_BACK = "0";
    public static final int FOCUS_HIDE = 101;
    /**
     * Timeout for the pre-capture sequence.
     */
    private static final long PRECAPTURE_TIMEOUT_MS = 1000;
    /**
     * Tolerance when comparing aspect ratios.
     */
    private static final double ASPECT_RATIO_TOLERANCE = 0.005;

    private static final String TAG = "Camera2RawFragment";

    /** Camera state: Device is closed. */
    private static final int STATE_CLOSED = 0;
    /** Camera state: Device is opened, but is not capturing. */
    private static final int STATE_OPENED = 1;
    /** Camera state: Showing camera preview. */
    private static final int STATE_PREVIEW = 2;
    /** Camera state: Waiting for 3A convergence before capturing a photo. */
    private static final int STATE_WAITING_FOR_3A_CONVERGENCE = 3;

    //the time needed to resume continuous focus mode after we tab to focus
    private static final int DELAY_TIME_RESUME_CONTINUOUS_AF = 1000;
    /**
     * A {@link Semaphore} to prevent the app from exiting before closing the camera.
     */
    private final Semaphore mCameraOpenCloseLock = new Semaphore(1);
    private final Object mCameraStateLock = new Object();
    //判断当前录制的屏幕是4:3模式 还是 16:9
    private boolean isRatio43;
    private OrientationEventListener mOrientationListener;
    /**
     * An {@link AutoFitTextureView} for camera preview.
     */
    private AutoFitTextureView mTextureView;
    private ImageView mIvFocus;
    //current auto focus mode,when we tap to focus, the mode will switch to auto
    private AutoFocusMode mControlAFMode = AutoFocusMode.CONTINUOUS_PICTURE;
    //focus zero region
//    private static final MeteringRectangle[] ZERO_WEIGHT_3A_REGION = AutoFocusHelper.getZeroWeightRegion();
//    private MeteringRectangle[] mAFRegions = ZERO_WEIGHT_3A_REGION;
//    private MeteringRectangle[] mAERegions = ZERO_WEIGHT_3A_REGION;

    enum AutoFocusMode {
        /**
         * System is continuously focusing.
         */
        CONTINUOUS_PICTURE,
        /**
         * System is running a triggered scan.
         */
        AUTO;

        int switchToCamera2FocusMode() {
            switch (this) {
                case CONTINUOUS_PICTURE:
                    return CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
                case AUTO:
                    return CameraMetadata.CONTROL_AF_MODE_AUTO;
                default:
                    return CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
            }
        }
    }

    /**
     * An additional thread for running tasks that shouldn't block the UI.  This is used for all
     * callbacks from the {@link CameraDevice} and {@link CameraCaptureSession}s.
     */
    private HandlerThread mBackgroundThread;
    /**
     * ID of the current {@link CameraDevice}.
     */
    private String mCameraId = CAMERA_BACK; // Default back camera
    /**
     * A {@link CameraCaptureSession } for camera preview.
     */
    private CameraCaptureSession mCaptureSession;
    /**
     * A reference to the open {@link CameraDevice}.
     */
    private CameraDevice mCameraDevice;
    /**
     * The {@link Size} of camera preview.
     */
    private Size mPreviewSize;
    /**
     * The {@link CameraCharacteristics} for the currently configured camera device.
     */
    private CameraCharacteristics mCharacteristics;
    /**
     * A {@link Handler} for running tasks in the background.
     */
    private Handler mBackgroundHandler;
    /**
     * A reference counted holder wrapping the {@link ImageReader} that handles JPEG image
     * captures. This is used to allow us to clean up the {@link ImageReader} when all background
     * tasks using its {@link Image}s have completed.
     */
    private ImageReader mJpegImageReader;
    /**
     * This a callback object for the {@link ImageReader}. "onImageAvailable" will be called when a
     * JPEG image is ready to be saved.
     */
    private OnImageListener onImageListener;
    public void setImageListener(OnImageListener listener) {
        onImageListener = listener;
    }
    private final ImageReader.OnImageAvailableListener mOnJpegImageAvailableListener = new ImageReader.OnImageAvailableListener() {

        @Override
        public void onImageAvailable(ImageReader reader) {
            showToast("hahahaha ");
            Logger.w("reader ", Thread.currentThread().getName());
            Image image = reader.acquireLatestImage();
            ByteBuffer buffer = image.getPlanes()[0].getBuffer();
            byte[] bytes = new byte[buffer.remaining()];
            buffer.get(bytes);
            image.close();

            if(onImageListener != null) {
                onImageListener.onImage(bytes, 0);
            }
        }

    };
    //Whether or not the currently configured camera device is fixed-focus.
    private boolean mAFRun = false;
    //记录需要截取的图片数量
    private int mPendingUserCaptures = 0;
    private CaptureRequest.Builder mPreviewRequestBuilder;
    /**
     * The state of the camera device.
     */
    private int mState = STATE_CLOSED;
    /**
     * Timer to use with pre-capture sequence to ensure a timely capture if 3A convergence is
     * taking too long.
     */
    private long mCaptureTimer;
    private CameraCaptureSession.CaptureCallback mPreCaptureCallback = new CameraCaptureSession.CaptureCallback() {

        private void process(CaptureResult result) {
            synchronized (mCameraStateLock) {
                switch (mState) {
                    case STATE_WAITING_FOR_3A_CONVERGENCE: {
                        boolean readyToCapture = true;
                        if (mAFRun) {
                            Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
                            if (afState == null) {
                                break;
                            }
                            // If auto-focus has reached locked state, we are ready to capture
                            readyToCapture = (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
                                            afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED);
                        }

                        // If we are running on an non-legacy device, we should also wait until
                        // auto-exposure and auto-white-balance have converged as well before
                        // taking a picture.
                        if (!isLegacyLocked()) {
                            Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
                            Integer awbState = result.get(CaptureResult.CONTROL_AWB_STATE);
                            if (aeState == null || awbState == null) {
                                break;
                            }

                            readyToCapture = readyToCapture &&
                                    aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED &&
                                    awbState == CaptureResult.CONTROL_AWB_STATE_CONVERGED;
                        }

                        // If we haven't finished the pre-capture sequence but have hit our maximum
                        // wait timeout, too bad! Begin capture anyway.
                        if (!readyToCapture && hitTimeoutLocked()) {
                            Log.w(TAG, "Timed out waiting for pre-capture sequence to complete.");
                            readyToCapture = true;
                        }

                        if (readyToCapture && mPendingUserCaptures > 0) {
                            // Capture once for each user tap of the "Picture" button.
                            while (mPendingUserCaptures > 0) {
                                captureStillPictureLocked();
                                mPendingUserCaptures--;
                            }
                            // After this, the camera will go back to the normal state of preview.
                            mState = STATE_PREVIEW;
                        }
                    }
                }
            }
        }

        @Override
        public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) {
            process(partialResult);
        }

        @Override
        public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
            process(result);
        }
    };
    private final TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {

        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
            configureTransform(width, height);
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
            configureTransform(width, height);
        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
            synchronized (mCameraStateLock) {
                mPreviewSize = null;
            }
            return true;
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture texture) {}
    };
    /**
     * {@link CameraDevice.StateCallback} is called when the currently active {@link CameraDevice}
     * changes its state.
     */
    private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {

        @Override
        public void onOpened(@NonNull CameraDevice cameraDevice) {
            // This method is called when the camera is opened.  We start camera preview here if
            // the TextureView displaying this has been set up.
            synchronized (mCameraStateLock) {
                mState = STATE_OPENED;
                mCameraOpenCloseLock.release();
                mCameraDevice = cameraDevice;

                StreamConfigurationMap map = mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                assert map != null;
                Size largestJpeg = findFitSize(map.getOutputSizes(ImageFormat.JPEG));//获取拍照图片的大小
                synchronized (mCameraStateLock) {
                    if(mJpegImageReader != null) {
                        mJpegImageReader.close();
                        mJpegImageReader = null;
                    }
                    mJpegImageReader =
                                ImageReader.newInstance(largestJpeg.getWidth(),
                                        largestJpeg.getHeight(), ImageFormat.JPEG, /*maxImages*/2);
                    mJpegImageReader.setOnImageAvailableListener(
                            mOnJpegImageAvailableListener, mBackgroundHandler);
                }
                // Start the preview session if the TextureView has been set up already.
                if (mPreviewSize != null && mTextureView.isAvailable()) {
                    createCameraPreviewSessionLocked();
                }
            }
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice cameraDevice) {
            releaseCamera(cameraDevice);
        }

        @Override
        public void onError(@NonNull CameraDevice cameraDevice, int error) {
            Log.e(TAG, "Received camera device error: " + error);
            releaseCamera(cameraDevice);
            Activity activity = getActivity();
            if (null != activity) {
                activity.finish();
            }
        }
        private void releaseCamera(CameraDevice cameraDevice) {
            synchronized (mCameraStateLock) {
                mState = STATE_CLOSED;
                mCameraOpenCloseLock.release();
                cameraDevice.close();
                mCameraDevice = null;
            }
        }
    };
    private final CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
        @Override
        public void onCaptureStarted(@NonNull CameraCaptureSession session,@NonNull  CaptureRequest request, long timestamp, long frameNumber) {}

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session,@NonNull  CaptureRequest request,@NonNull  TotalCaptureResult result) {
            synchronized (mCameraStateLock) {
                finishedCaptureLocked();
            }
            showToast("Saving JPEG as: ");
        }

        @Override
        public void onCaptureFailed(@NonNull CameraCaptureSession session,@NonNull CaptureRequest request,@NonNull CaptureFailure failure) {
            synchronized (mCameraStateLock) {
                finishedCaptureLocked();
            }
            showToast("Capture failed!");
        }
    };

    /**
     * Return true if the two given {@link Size}s have the same aspect ratio.
     *
     * @param a first {@link Size} to compare.
     * @param b second {@link Size} to compare.
     * @return true if the sizes have the same aspect ratio, otherwise false.
     */
    private static boolean checkAspectsEqual(Size a, Size b) {
        double aAspect = a.getWidth() / (double) a.getHeight();
        double bAspect = b.getWidth() / (double) b.getHeight();
        return Math.abs(aAspect - bAspect) <= ASPECT_RATIO_TOLERANCE;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return new AutoFitTextureView(inflater.getContext());
    }

    @Override
    public void onViewCreated(final View view, Bundle savedInstanceState) {
        mTextureView = (AutoFitTextureView) view;
//        mIvFocus = (ImageView) view.findViewById(R.id.iv_focus);

        mTextureView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int actionMasked = MotionEventCompat.getActionMasked(event);
                int fingerX, fingerY;
                int length = (int) (getResources().getDisplayMetrics().density * 80);
                switch (actionMasked) {
                    case MotionEvent.ACTION_DOWN:
//                        fingerX = (int) event.getX();
//                        fingerY = (int) event.getY();
//
//                        mIvFocus.setX(fingerX - length / 2);
//                        mIvFocus.setY(fingerY - length / 2);
//
//                        mIvFocus.setVisibility(View.VISIBLE);
//                        triggerFocusArea(fingerX, fingerY);

                        break;
                }

                return false;
            }
        });
        mOrientationListener = new OrientationEventListener(getActivity(), SensorManager.SENSOR_DELAY_NORMAL) {
            @Override
            public void onOrientationChanged(int orientation) {
                if (mTextureView != null && mTextureView.isAvailable()) {
                    configureTransform(mTextureView.getWidth(), mTextureView.getHeight());
                }
            }
        };
    }

    @Override
    public void onResume() {
        super.onResume();
        startBackgroundThread();
        openCamera();

        if (mTextureView.isAvailable()) {
            configureTransform(mTextureView.getWidth(), mTextureView.getHeight());
        } else {
            mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
        }
        if (mOrientationListener != null && mOrientationListener.canDetectOrientation()) {
            mOrientationListener.enable();
        }
    }

    @Override
    public void onPause() {
        if (mOrientationListener != null) {
            mOrientationListener.disable();
        }
        closeCamera();
        stopBackgroundThread();
        super.onPause();
    }

    private void triggerFocusArea(float x, float y) {
//        CameraManager manager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);
//        try {
//            CameraCharacteristics characteristics
//                    = manager.getCameraCharacteristics(mCameraId);
//            Integer sensorOrientation = characteristics.get(
//                    CameraCharacteristics.SENSOR_ORIENTATION);
//
//            sensorOrientation = sensorOrientation == null ? 0 : sensorOrientation;
//
//            Rect cropRegion = AutoFocusHelper.cropRegionForZoom(characteristics, 1f);
//            mAERegions = AutoFocusHelper.aeRegionsForNormalizedCoord(x, y, cropRegion, sensorOrientation);
//            mAFRegions = AutoFocusHelper.afRegionsForNormalizedCoord(x, y, cropRegion, sensorOrientation);
//
//            // Step 1: Request single frame CONTROL_AF_TRIGGER_START.
//            CaptureRequest.Builder builder;
//            builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
//            builder.addTarget(mPreviewSurface);
//            builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
//
//            mControlAFMode = AutoFocusMode.AUTO;
//
//            builder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode.switchToCamera2FocusMode());
//            builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
//            mCaptureSession.capture(builder.build(), mPreCaptureCallback, mBackgroundHandler);
//
//            // Step 2: Call repeatingPreview to update mControlAFMode.
//            sendRepeatPreviewRequest();
//            resumeContinuousAFAfterDelay(DELAY_TIME_RESUME_CONTINUOUS_AF);
//        } catch (CameraAccessException ex) {
//            Log.e(TAG, "Could not execute preview request.", ex);
//        }
    }

    private void resumeContinuousAFAfterDelay(int timeMillions) {
        mBackgroundHandler.removeCallbacks(mResumePreviewRunnable);
        mBackgroundHandler.postDelayed(mResumePreviewRunnable, timeMillions);
    }

    //the runnable to resume continuous focus mode after tab to focus
    private Runnable mResumePreviewRunnable = new Runnable() {
        @Override
        public void run() {
//            mAERegions = ZERO_WEIGHT_3A_REGION;
//            mAFRegions = ZERO_WEIGHT_3A_REGION;
//            mControlAFMode = AutoFocusMode.CONTINUOUS_PICTURE;
//            if (mCameraDevice != null)
//                sendRepeatPreviewRequest();
//            Message msg = Message.obtain();
//            mHandler.sendEmptyMessage(FOCUS_HIDE);
        }
    };

    private boolean sendRepeatPreviewRequest() {
//        try {
//            CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
//            builder.addTarget(mPreviewRequestBuilder);
//            builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
//            builder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode.switchToCamera2FocusMode());

//            mCaptureSession.setRepeatingRequest(builder.build(), mPreCaptureCallback, mBackgroundHandler);
            return true;
//        } catch (CameraAccessException e) {
//            e.printStackTrace();
//            return false;
//        }
    }

    public void switchCamera() {
        if (mCameraId.equals(CAMERA_FRONT)) {
            mCameraId = CAMERA_BACK;
            closeCamera();
            reopenCamera();

        } else if (mCameraId.equals(CAMERA_BACK)) {
            mCameraId = CAMERA_FRONT;
            closeCamera();
            reopenCamera();
        }
    }

    private void reopenCamera() {
        if (mTextureView.isAvailable()) {
            openCamera();
        } else {
            mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
        }
    }

    @SuppressLint("MissingPermission")
    private void openCamera() {
        Activity activity = getActivity();
        CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
        if (manager == null) {
            CameraUtils.buildErrorDialog(getFragmentManager(), "This device doesn't support Camera2 API.");
            return;
        }
        try {
            if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
                throw new RuntimeException("Time out waiting to lock camera opening.");
            }
            mCharacteristics = manager.getCameraCharacteristics(mCameraId);
            manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
            ToastUtil.showMsg("Camera open error...");
        } catch (InterruptedException e) {
            throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
        }
    }

    private void closeCamera() {
        try {
            mCameraOpenCloseLock.acquire();
            synchronized (mCameraStateLock) {
                mPendingUserCaptures = 0;
                mState = STATE_CLOSED;
                CameraUtils.close(mCaptureSession);
                CameraUtils.close(mCameraDevice);
                CameraUtils.close(mJpegImageReader);
            }
        } catch (InterruptedException e) {
            throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
        } finally {
            mCameraOpenCloseLock.release();
        }
    }

    /**
     * Starts a background thread and its {@link Handler}.
     */
    private void startBackgroundThread() {
        mBackgroundThread = new HandlerThread("CameraBackground");
        mBackgroundThread.start();
        synchronized (mCameraStateLock) {
            mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
        }
    }

    /**
     * Stops the background thread and its {@link Handler}.
     */
    private void stopBackgroundThread() {
        mBackgroundThread.quitSafely();
        try {
            mBackgroundThread.join();
            mBackgroundThread = null;
            synchronized (mCameraStateLock) {
                mBackgroundHandler = null;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Creates a new {@link CameraCaptureSession} for camera preview.
     * <p/>
     * Call this only with {@link #mCameraStateLock} held.
     */
    private void createCameraPreviewSessionLocked() {
        try {
            SurfaceTexture texture = mTextureView.getSurfaceTexture();
            // We configure the size of default buffer to be the size of camera preview we want.
            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());

            Surface mPreviewSurface = new Surface(texture);
            mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mPreviewRequestBuilder.addTarget(mPreviewSurface);
            mCameraDevice.createCaptureSession(Arrays.asList(mPreviewSurface,
                    mJpegImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
                        @Override
                        public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                            synchronized (mCameraStateLock) {
                                // The camera is already closed
                                if (null == mCameraDevice) {
                                    return;
                                }

                                try {
                                    mAFRun = CameraUtils.setup3AControlsLocked(mPreviewRequestBuilder, mCharacteristics);
                                    // Default hdr off
                                    mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_DISABLED);
                                    // Finally, we start displaying the camera preview.
                                    cameraCaptureSession.setRepeatingRequest(
                                            mPreviewRequestBuilder.build(),
                                            mPreCaptureCallback, mBackgroundHandler);
                                    mState = STATE_PREVIEW;
                                } catch (CameraAccessException | IllegalStateException e) {
                                    e.printStackTrace();
                                    return;
                                }
                                // When the session is ready, we start displaying the preview.
                                mCaptureSession = cameraCaptureSession;
                            }
                        }

                        @Override
                        public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
                            showToast("Failed to configure camera.");
                        }
                    }, mBackgroundHandler
            );
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * Configure the necessary {@link Matrix} transformation to `mTextureView`,
     * and start/restart the preview capture session if necessary.
     * <p/>
     * This method should be called after the camera state has been initialized in
     * setUpCameraOutputs.
     *
     * @param viewWidth  The width of `mTextureView`
     * @param viewHeight The height of `mTextureView`
     */
    private void configureTransform(int viewWidth, int viewHeight) {
        isRatio43 = CameraUtils.aspectRatio(viewWidth, viewHeight);
        Activity activity = getActivity();
        synchronized (mCameraStateLock) {
            if (null == mTextureView || null == activity || mCharacteristics == null) {
                return;
            }
            StreamConfigurationMap map = mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            Size largestJpeg = findFitSize(map.getOutputSizes(ImageFormat.JPEG));//获取拍照图片的大小
            Size previewSize = findFitSize(map.getOutputSizes(SurfaceTexture.class));//获取相机预览的大小
            synchronized (mCameraStateLock) {
                if (mJpegImageReader == null) {
                    mJpegImageReader =
                            ImageReader.newInstance(largestJpeg.getWidth(),
                                    largestJpeg.getHeight(), ImageFormat.JPEG, /*maxImages*/2);
                }
                mJpegImageReader.setOnImageAvailableListener(
                        mOnJpegImageAvailableListener, mBackgroundHandler);
            }
            // Find the rotation of the device relative to the native device orientation.
            int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
            Point displaySize = new Point();
            activity.getWindowManager().getDefaultDisplay().getSize(displaySize);
            int totalRotation = CameraUtils.sensorToDeviceRotation(mCharacteristics, deviceRotation);
            // Swap the view dimensions for calculation as needed if they are rotated relative to the sensor.
            boolean swappedDimensions = totalRotation == 90 || totalRotation == 270;
            if (swappedDimensions) {
                mTextureView.setAspectRatio(previewSize.getHeight(), previewSize.getWidth());
            } else {
                mTextureView.setAspectRatio(previewSize.getWidth(), previewSize.getHeight());
            }
            if (mPreviewSize == null || !checkAspectsEqual(previewSize, mPreviewSize)) {
                mPreviewSize = previewSize;
                if (mState != STATE_CLOSED) {
                    createCameraPreviewSessionLocked();
                }
            }
        }
    }

    public void takePicture() {
        synchronized (mCameraStateLock) {
            mPendingUserCaptures++;
            if (mState != STATE_PREVIEW) {
                return;
            }
            try {
                // Trigger an auto-focus run if camera is capable. If the camera is already focused,
                // this should do nothing.
                if (mAFRun) {
                    mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
                }
                // If this is not a legacy device, we can also trigger an auto-exposure metering run.
                if (!isLegacyLocked()) {
                    // Tell the camera to lock focus.
                    mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_START);
                }
                mState = STATE_WAITING_FOR_3A_CONVERGENCE;
                startTimerLocked();
                mCaptureSession.capture(mPreviewRequestBuilder.build(), mPreCaptureCallback, mBackgroundHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Send a capture request to the camera device that initiates a capture targeting the JPEG outputs.
     * <p/>
     * Call this only with {@link #mCameraStateLock} held.
     */
    private void captureStillPictureLocked() {
        final Activity activity = getActivity();
        if (null == activity || null == mCameraDevice) {
            return;
        }
        try {
            // This is the CaptureRequest.Builder that we use to take a picture.
            final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            captureBuilder.addTarget(mJpegImageReader.getSurface());
            // Use the same AE and AF modes as the preview.
            CameraUtils.setup3AControlsLocked(captureBuilder, mCharacteristics);
            // Set orientation.
//            int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
//            captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, sensorToDeviceRotation(mCharacteristics, rotation));
            CaptureRequest request = captureBuilder.build();
            mCaptureSession.capture(request, mCaptureCallback, mBackgroundHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * Called after a RAW/JPEG capture has completed; resets the AF trigger state for the pre-capture sequence.
     * <p/>
     * Call this only with {@link #mCameraStateLock} held.
     */
    private void finishedCaptureLocked() {
        try {
            // Reset the auto-focus trigger in case AF didn't run quickly enough.
            if (mAFRun) {
                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
                mCaptureSession.capture(mPreviewRequestBuilder.build(), mPreCaptureCallback, mBackgroundHandler);
                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE);
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private void showToast(final String text) {
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getActivity(), text, Toast.LENGTH_SHORT).show();
            }
        });
    }

    /**
     * Check if we are using a device that only supports the LEGACY hardware level.
     * <p/>
     * Call this only with {@link #mCameraStateLock} held.
     *
     * @return true if this is a legacy device.
     */
    private boolean isLegacyLocked() {
        return mCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) ==
                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
    }

    /**
     * Start the timer for the pre-capture sequence.
     * <p/>
     * Call this only with {@link #mCameraStateLock} held.
     */
    private void startTimerLocked() {
        mCaptureTimer = SystemClock.elapsedRealtime();
    }

    /**
     * Check if the timer for the pre-capture sequence has been hit.
     * <p/>
     * Call this only with {@link #mCameraStateLock} held.
     *
     * @return true if the timeout occurred.
     */
    private boolean hitTimeoutLocked() {
        return (SystemClock.elapsedRealtime() - mCaptureTimer) > PRECAPTURE_TIMEOUT_MS;
    }

    private void showIamge(Bitmap bitmap) {
    }

    /**
     * A dialog that explains about the necessary permissions.
     */
    private static class Camera2Handler extends Handler {
        private WeakReference<Camera2Fragment> fragment;

        public Camera2Handler(Camera2Fragment fragment) {
            this.fragment = new WeakReference<>(fragment);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case FOCUS_HIDE:
                    fragment.get().mIvFocus.setVisibility(View.INVISIBLE);
                    break;
            }
        }
    }
    private Size findFitSize(Size[] sizes) {
        if(sizes == null) throw new IllegalStateException("not found size for jpg");
        Size maxArea = null;
        for(Size size : sizes) {
            if(isRatio43 != CameraUtils.aspectRatio(size.getWidth(), size.getHeight())) continue;
            if(maxArea == null) maxArea = size;
            else {
                if(maxArea.getWidth() < size.getWidth() && maxArea.getHeight() < size.getHeight()) maxArea = size;
            }
        }
        return maxArea == null ? sizes[0] : maxArea;
    }
}